package com.gitee.apanlh.util.net.http;

import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.func.FuncMapExecute;
import com.gitee.apanlh.util.log.LogLevel;
import com.gitee.apanlh.util.net.http.auth.HttpAuth;
import com.gitee.apanlh.util.net.http.handler.HttpInterceptorContext;
import com.gitee.apanlh.util.net.http.handler.HttpRequestInterceptor;
import com.gitee.apanlh.util.net.http.handler.HttpRequestPreInterceptor;
import com.gitee.apanlh.util.net.http.handler.HttpResponseInterceptor;
import com.gitee.apanlh.util.net.http.log.HttpLog;
import com.gitee.apanlh.util.net.http.model.FormDataFileResource;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.gitee.apanlh.web.http.HttpContentType;
import com.gitee.apanlh.web.http.HttpMethod;

import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;

/**	
 * 	HTTP构建器
 * 	<br>用于HTTP之间调用
 * 	<br>如果只是简单的HTTP调用则可以使用{@link HttpUtils}类来完成
 * 	TODO #增加配置，自定义配置是否把参数进行URLEncode方法
 *
 * 	@author Pan
 */
public class HttpClientBuilder {
	
	/** 请求对象 */
	private HttpRequest request;
	/** 执行器 */
	private HttpExecutor executor;
	/** 拦截器上下文 */
	private HttpInterceptorContext interceptorContext;
    	
	/**
	 * 	默认构造
	 * 	
	 * 	@author Pan
	 */
	HttpClientBuilder() {
		super();
	}

	/**	
	 * 	构造-默认GET请求
	 * 
	 * 	@author Pan
	 * 	@param 	url			请求地址
	 */
	HttpClientBuilder(String url) {
		this(url, HttpMethod.GET);
	}
	
	/**	
	 * 	构造-自定义请求
	 * 	<br>初始化参数
	 * 	<br>加载默认配置
	 *  	
	 * 	@author Pan
	 * 	@param 	url			请求地址
	 * 	@param 	httpMethod	请求方法
	 */
	HttpClientBuilder(String url, HttpMethod httpMethod) {
		this.request = HttpRequest.create(HttpUrl.create(url), httpMethod);
		this.request.setConfig(HttpConfig.create(request.getMethod()));
		this.interceptorContext = new HttpInterceptorContext();
		this.executor = HttpExecutor.create(request, interceptorContext);
	}
	
	/**	
	 * 	填充-HTTP认证请求头
	 *  <br>默认实现
	 *  <br>如果需要强自定义自行添加header{{@link #withHeader(String, String)}}
	 *  
	 * 	@author Pan
	 * 	@param 	httpAuth	HTTP认证类
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withAuth(HttpAuth httpAuth) {
		withHeader(httpAuth.getHeaderKey(), httpAuth.getHeaderValue());
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求头
	 *  <br>以添加单个值形式填充请求头内容
	 *  
	 * 	@author Pan
	 * 	@param 	key		键
	 * 	@param 	value	值
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withHeader(String key, String value) {
		HttpRequestHeader header = request.getHeader();
		if (header == null) {
			request.setHeader(HttpRequestHeader.create(key, value));
			return this;
		}
		header.add(key, value);
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求头
	 *  <br>以HttpHeader对象来填充请求头内容
	 *  
	 * 	@author Pan
	 * 	@param 	httpHeader	请求头对象
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withHeader(HttpRequestHeader httpHeader) {
		request.setHeader(httpHeader);
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求头
	 *  <br>以Map形式填充请求头内容
	 * 
	 * 	@author Pan
	 * 	@param 	map		Map
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withHeader(Map<String, String> map) {
		request.setHeader(HttpRequestHeader.create(map));
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求头
	 *  <br>以Map形式填充请求头内容
	 * 	<br>函数式
	 * 	
	 * 	@author Pan
	 * 	@param 	execute	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withHeader(FuncMapExecute<String, String> execute) {
		Map<String, String> newMap = MapUtils.newHashMap();
		execute.execute(newMap);
		return withHeader(newMap);
	}
	
	/**
	 * 	填充-查询请求参数
	 * 	<br>将值解析成QueryParam形式，附在URL形式上
	 * 	<br>可用于GET请求参数
	 * 	
	 * 	@author Pan
	 * 	@param  <T> 数据类型
	 * 	@param 	t	对象
	 * 	@return	HttpClientBuilder
	 */
	public <T> HttpClientBuilder withQueryParams(T t) {
		if (t == null) {
			return this;
		}
		
		request.setBody(HttpBody.create(request, t, HttpBodyTypeEnum.QUERY_PARAMS));
		return this;
	}
	
	/**	
	 * 	填充-查询请求参数
	 * 	<br>将值解析成QueryParam形式，附在URL形式上
	 * 	<br>可用于GET请求参数
	 * 	
	 * 	@author Pan
	 *  @param  <K>     键类型
	 *  @param  <V>     值类型
	 * 	@param 	funcMapExecute	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public <K, V> HttpClientBuilder withQueryParams(FuncMapExecute<K, V> funcMapExecute) {
		Map<K, V> newMap = MapUtils.newLinkedHashMap();
		funcMapExecute.execute(newMap);
		return withQueryParams(newMap);
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将值解析成QueryParam形式，附在URL形式上
	 * 	<br>便于添加单值来发送请求
	 * 
	 * 	@author Pan
	 *  @param  <V>     值类型
	 * 	@param 	key		键
	 * 	@param 	value	值
	 * 	@return	HttpClientBuilder
	 */
	public <V> HttpClientBuilder withQueryParams(String key, V value) {
		return withQueryParams(Collections.singletonMap(key, value));
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将对象解析后以URL RESTFul格式
	 * 	<br>注意如果是Bean形式请注意值的顺序，以及值的边界
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	t		对象
	 * 	@return	HttpClientBuilder
	 */
	public <T> HttpClientBuilder withPathVariable(T t) {
		if (t == null) {
			return this;
		}
		
		//	开启RESTful风格 默认关闭
		getConfig().setHasEnableRest(true);
		request.setBody(HttpBody.create(request, t, HttpBodyTypeEnum.PATH_VARIABLE));
		return this;
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将Map解析后以URL RESTFul风格
	 * 	<br>函数式
	 * 	<br>注意如果是Bean形式请注意值的顺序，以及值的边界
	 * 	
	 * 	@author Pan
	 *  @param  <K>     键类型
	 *  @param  <V>     值类型
	 * 	@param 	funcMapExecute 	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public <K, V> HttpClientBuilder withPathVariable(FuncMapExecute<K, V> funcMapExecute) {
		Map<K, V> newMap = MapUtils.newLinkedHashMap();
		funcMapExecute.execute(newMap);
		return withPathVariable(newMap);
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将值解析至URL RESTFul风格
	 * 	<br>便于添加单值来发送请求
	 * 	<br>注意如果是Bean形式请注意值的顺序，以及值的边界
	 * 	
	 * 	@author Pan
	 *  @param  <V>     值类型
	 * 	@param 	key		键
	 * 	@param 	value	值
	 * 	@return	HttpClientBuilder
	 */
	public <V> HttpClientBuilder withPathVariable(String key, V value) {
		return withPathVariable(Collections.singletonMap(key, value));
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>form表单提交
	 * 	<br>将对象解析后以Form格式发送
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	t	对象
	 * 	@return	HttpClientBuilder
	 */
	public <T> HttpClientBuilder withBodyForm(T t) {
		if (t == null) {
			return this;
		}
		
		request.setBody(HttpBody.create(request, t, HttpBodyTypeEnum.FORM));
		return this;
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>form表单提交
	 * 	<br>将Map解析后以Form格式发送
	 * 
	 * 	@author Pan
	 *  @param  <K>     键类型
	 *  @param  <V>     	值类型
	 * 	@param 	funcMapExecute 	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public <K, V> HttpClientBuilder withBodyForm(FuncMapExecute<K, V> funcMapExecute) {
		Map<K, V> newMap = MapUtils.newLinkedHashMap();
		funcMapExecute.execute(newMap);
		return withBodyForm(newMap);
	}
	
	/**	
	 * 	FORM形式填充
	 * 	<br>form表单提交
	 * 	<br>便于添加单值来发送请求
	 * 
	 * 	@author Pan
	 *  @param  <V>     值类型
	 * 	@param 	key		键
	 * 	@param 	value	值
	 * 	@return	HttpClientBuilder
	 */
	public <V> HttpClientBuilder withBodyForm(String key, V value) {
		return withBodyForm(Collections.singletonMap(key, value));
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>可填充JavaBean形式，将JavaBean内自动解析(byte[]则没有文件名称默认file文件名称,建议将byte[]改为{@link FormDataFileResource}对象)
	 * 	<br>Value格式不限于对象/字节数组/文件对象
	 * 	<br>如果传递对象将解析对象值
	 * 	<br>将对象解析后以FormData格式发送
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	t	对象
	 * 	@return	HttpClientBuilder
	 */
	public <T> HttpClientBuilder withBodyFormData(T t) {
		if (t == null) {
			return this;
		}
		
		request.setBody(HttpBody.create(request, t, HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>Key会作为来源文件名称使用
	 * 	<br>Value格式不限于对象/字节数组/文件对象
	 * 
	 * 	@author Pan
	 *  @param  <V>     值类型
	 * 	@param 	funcMapExecute 	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public <V> HttpClientBuilder withBodyFormData(FuncMapExecute<String, V> funcMapExecute) {
		Map<String, V> newMap = MapUtils.newLinkedHashMap();
		funcMapExecute.execute(newMap);
		request.setBody(HttpBody.create(request, newMap, HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以字节形式进行发送FormData
	 * 	<br>key为file
	 * 	<br>来源文件名为file
	 * 	
	 * 	@author Pan
	 * 	@param 	bytes	字节数组
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(byte[] bytes) {
		if (bytes == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, Collections.singletonMap("file", bytes), HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以字节形式进行发送FormData
	 * 	<br>自定义来源文件名称
	 * 
	 * 	@author Pan
	 * 	@param 	fileName	文件名称
	 * 	@param 	bytes		字节数组
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(String fileName, byte[] bytes) {
		if (bytes == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, Collections.singletonMap(fileName, bytes), HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以文件形式进行发送FormData
	 * 	<br>来源名称为File对象的文件名称
	 * 	
	 * 	@author Pan
	 * 	@param 	file	文件
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(File file) {
		if (file == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, Collections.singletonMap("file", file), HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以文件形式进行发送FormData
	 * 	<br>key为file,多个file文件名示例：file，file2，file3，file4
	 * 	<br>来源名称为File对象的文件名称
	 * 
	 * 	@author Pan
	 * 	@param 	files	多文件
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(File... files) {
		if (ValidParam.isEmpty(files)) {
			return this;
		}
		
		return withBodyFormData(newMap -> {
			for (int i = 0; i < files.length; i++) {
				if (i > 0) {
					newMap.put("file" + ClassConvertUtils.toStr(i + 1), files[i]);
				} else {
					newMap.put("file", files[i]);
				}
			}
		}) ;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以流形式进行发送FormData
	 * 	<br>自定义来源文件名称
	 * 
	 * 	@author Pan
	 * 	@param 	fileName		文件名称
	 * 	@param 	inputStream		流
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(String fileName, InputStream inputStream) {
		if (inputStream == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, Collections.singletonMap(fileName, inputStream), HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	FormData形式填充
	 * 	<br>将以FormData资源对象进行填充
	 * 	
	 * 	@author Pan
	 * 	@param 	formDataFileResource	文件资源
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyFormData(FormDataFileResource formDataFileResource) {
		if (formDataFileResource == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, Collections.singletonMap("file", formDataFileResource), HttpBodyTypeEnum.FORM_DATA));
		return this;
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将对象解析后以JSON格式发送
	 * 	<br>如果请求为GET则自动拼接URL后
	 * 
	 * 	@author Pan
	 *  @param  <T>     数据类型
	 * 	@param 	t		对象
	 * 	@return	HttpClientBuilder
	 */
	public <T> HttpClientBuilder withBodyJson(T t) {
		if (t == null) {
			return this;
		}
		
		request.setBody(HttpBody.create(request, t, HttpBodyTypeEnum.JSON));
		return this;
	}
	
	/**
	 * 	填充-HTTP填充Body
	 * 	<br>要求必须为JSON格式
	 * 	<br>如果请求为GET则自动拼接URL后
	 * 
	 * 	@author Pan
	 * 	@param 	str	字符串
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withBodyJson(String str) {
		if (str == null) {
			return this;
		}
		request.setBody(HttpBody.create(request, str, HttpBodyTypeEnum.JSON));
		request.getBody().setParameterParse(true);
		return this;
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>将Map解析后以JSON格式发送
	 * 	<br>函数式
	 * 
	 * 	@author Pan
	 *  @param  <K>     键类型
	 *  @param  <V>     值类型
	 * 	@param 	funcMapExecute 	执行方法
	 * 	@return	HttpClientBuilder
	 */
	public <K, V> HttpClientBuilder withBodyJson(FuncMapExecute<K, V> funcMapExecute) {
		Map<K, V> newMap = MapUtils.newLinkedHashMap();
		funcMapExecute.execute(newMap);
		return withBodyJson(newMap);
	}
	
	/**	
	 * 	填充-HTTP填充Body
	 * 	<br>便于添加单值来发送请求
	 * 
	 * 	@author Pan
	 *  @param  <V>     值类型
	 * 	@param 	key		键
	 * 	@param 	value	值
	 * 	@return	HttpClientBuilder
	 */
	public <V> HttpClientBuilder withBodyJson(String key, V value) {
		return withBodyJson(Collections.singletonMap(key, value));
	}
	
	/**	
	 * 	字段格式化
	 * 	<br>默认遵循小驼峰形式
	 * 	
	 * 	@author Pan
	 * 	@param 	targetFormat	目标格式
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withFieldFormat(HttpBodyFormat targetFormat) {
		return withFieldFormat(null, targetFormat);
	}

	/**	
	 * 	字段格式化
	 * 	<br>自定义格式
	 * 	#mark 需要补充
	 * 	
	 * 	@author Pan
	 * 	@param 	sourceFormat	原格式
	 * 	@param 	targetFormat	目标格式
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withFieldFormat(HttpBodyFormat sourceFormat, HttpBodyFormat targetFormat) {
		return this;
	}
	
	/**
	 * 	设置连接超时参数(单位毫秒)
	 * 	
	 * 	@author Pan
	 * 	@param 	connectTimeout	连接超时参数(单位毫秒)
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withConnectTimeout(int connectTimeout) {
		getConfig().setConnectTimeout(connectTimeout);
		return this;
	}
	
	/**
	 * 	设置读取超时参数(单位毫秒)
	 * 	
	 * 	@author Pan
	 * 	@param 	readTimeout	读取超时参数(单位毫秒)
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withReadTimeout(int readTimeout) {
		getConfig().setReadTimeout(readTimeout);
		return this;
	}
	
	/**	
	 * 	自定义HTTP传输字符集编码
	 * 	<br>默认UTF-8
	 * 	
	 * 	@author Pan
	 * 	@param 	charset	字符集编码
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withCharset(String charset) {
		if (ValidParam.isEmpty(charset)) {
			return this;
		}
		
		getConfig().setCharset(charset);
		return this;
	}

	/**	
	 * 	填充-自定义请求头类型
	 * 	<br>只需要填充value值无需填充Content-Type
	 * 	<br>如使用withBodyForm则会自动添加form请求头，withBodyJson会自动添加json请求头
	 * 	
	 * 	@author Pan
	 * 	@param 	contentType	请求头类型
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withContentType(String contentType) {
		withHeader(HttpContentType.CONTENT_TYPE, contentType);
		return this;
	}
	
	/**	
	 * 	填充-请求头类型
	 * 	<br>如使用withBodyForm则会自动添加form请求头，withBodyJson会自动添加json请求头
	 * 
	 * 	@author Pan
	 * 	@param 	contentType	请求头枚举
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withContentType(HttpContentType contentType) {
		withHeader(HttpContentType.CONTENT_TYPE, contentType.getType());
		return this;
	}
	
	/**	
	 * 	填充日志链路ID
	 * 	<br>自定义id
	 * 
	 * 	@author Pan
	 * 	@param 	id	ID
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withLogTraceId(Long id) {
		executor.getHttpLog().setTraceId(id);
		return this;
	}
	
	/**	
	 * 	填充-记录日志等级
	 * 	<br>请求以及响应日志
	 * 	<br>默认输出DEBUG级别
	 * 	
	 * 	@author Pan
	 * 	@param 	logLevel 日志等级
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withLogLevel(LogLevel logLevel) {
		this.getRequest().getConfig().setLogLevel(logLevel);
		return this;
	}
	
	/**	
	 * 	填充-错误响应日志等级
	 * 	<br>出现4xx及5xx系列
	 * 	<br>默认输出ERROR级别
	 * 	
	 * 	@author Pan
	 * 	@param 	logLevel 日志等级
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withErrorLogLevel(LogLevel logLevel) {
		this.getRequest().getConfig().setErrorLogLevel(logLevel);
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求拦截器(参数解析前)
	 * 	<br>获取的参数是原始参数
	 * 	<br>此拦截器是在对请求体参数解析前进行操作
	 * 	
	 * 	@author Pan
	 * 	@param 	httpRequestPreInterceptor HTTP请求拦截器(参数解析前)
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withRequestPreInterceptor(HttpRequestPreInterceptor httpRequestPreInterceptor) {
		interceptorContext.addRequestPreInterceptor(httpRequestPreInterceptor);
		return this;
	}
	
	/**	
	 * 	填充-HTTP请求拦截器(参数解析后)
	 * 	<br>获取的参数则是已经解析过后的参数
	 * 	<br>此拦截器是在对请求体参数解析后进行操作
	 * 	<br>如果要重置参数则可以直接更改HttpBody
	 * 	<br>如果使用的是Restful风格或者QueryParam方式请使用{@link HttpClientBuilder#withRequestPreInterceptor(HttpRequestPreInterceptor httpRequestPreInterceptor)} 因为即使修改了请求体，也不会再次刷新URL资源
	 * 
	 * 	@author Pan
	 * 	@param 	httpRequestInterceptor HTTP请求拦截器(参数解析后)
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withRequestInterceptor(HttpRequestInterceptor httpRequestInterceptor) {
		interceptorContext.addRequestInterceptor(httpRequestInterceptor);
		return this;
	}
	
	/**	
	 * 	填充-HTTP响应拦截器
	 * 	<br>不会对响应体做出任何改动
	 * 	
	 * 	@author Pan
	 * 	@param 	httpResponseInterceptor HTTP响应拦截器
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder withResponseInterceptor(HttpResponseInterceptor httpResponseInterceptor) {
		interceptorContext.addResponseInterceptor(httpResponseInterceptor);
		return this;
	}

	/**
	 * 	关闭请求发送时对参数进行urlEncode处理
	 * 	<br>默认开启
	 *
	 * 	@author Pan
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder disableParamUrlEncode() {
		getConfig().setHasParamUrlEncode(false);
		return this;
	}

	/**
	 * 	关闭请求发送时对参数进行urlDecode处理
	 * 	#mark TODO 补充
	 * 	<br>默认开启
	 *
	 * 	@author Pan
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder disableParamUrlDecode() {
		return this;
	}

	/**	
	 * 	关闭请求及相应日志记录
	 * 	
	 * 	@author Pan
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder disableLog() {
		disableRequestLog();
		disableResponseLog();
		return this;
	}
	
	/**	
	 * 	关闭发送时记录请求体内容
	 * 	<br>默认打开状态
	 * 	<br>使用log记录发送请求参数，并且生成相关记录ID便于查找
	 * 
	 * 	@author Pan
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder disableRequestLog() {
		getConfig().setWriteRequestLog(false);
		return this;
	}
	
	/**	
	 * 	关闭响应时记录响应体内容
	 * 	<br>默认打开状态
	 * 	<br>使用log记录响应体内容，并且生成相关记录ID便于查找
	 * 	<br>根据调用{@link #build()} 返回的HttpResponse类中的getStr(), getByte()方法来记录打印数据
	 *	<br>首次调用getStr()将返回String类型, 
	 *	<br>首次调用getByte()将返回Byte[]类型返回长度
	 *	
	 * 	@author Pan
	 * 	@return	HttpClientBuilder
	 */
	public HttpClientBuilder disableResponseLog() {
		getConfig().setWriteResponseLog(false);
		return this;
	}
	
	/**			
	 * 	获取请求HTTP日志
	 * 	
	 * 	@author Pan
	 * 	@return	HttpRequest
	 */
	public HttpLog getLog() {
		return executor.getHttpLog();
	}
	
	/**			
	 * 	获取请求对象
	 * 	
	 * 	@author Pan
	 * 	@return	HttpRequest
	 */
	public HttpRequest getRequest() {
		return request;
	}
	
	/**			
	 * 	获取客户端配置
	 * 	
	 * 	@author Pan
	 * 	@return	HttpConfig
	 */
	public HttpConfig getConfig() {
		return request.getConfig();
	}
	
	/**	
	 * 	构建-将发送HTTP请求
	 * 	<p>返回响应对象
	 * 	<p>在连接过程中如果出现异常将关闭自动连接
	 * 	<p>自动关闭流及客户端连接
	 * 	<br>自动适配请求格式头
	 * 	<br>请求错误将抛出异常
	 * 	
	 * 	@author Pan
	 * 	@return	HttpResponse
	 */
	public HttpResponse build() {
		//	非GET请求时验证
		if (!Eq.enums(HttpMethod.GET, request.getMethod())) {
			Assert.isNotNullThrows(request.getBody(), new IllegalArgumentException("please check if the request body is null"));
		}
		return executor.execute();
	}
	
	/**	
	 * 	构造-默认GET请求
	 * 	<p>自动关闭流及客户端连接
	 * 	<br>自动装配默认配置
	 * 	<br>自动适配请求格式头
	 * 
	 * 	@author Pan
	 * 	@param 	url	请求地址
	 * 	@return	HttpClientBuilder
	 */
	public static HttpClientBuilder builder(String url) {
		return builder(url, HttpMethod.GET);
	}
	
	/**	
	 * 	构造-自定义请求
	 * 	<p>自动关闭流及客户端连接
	 * 	<br>自动装配默认配置
	 * 	<br>自动适配请求格式头
	 * 
	 * 	@author Pan
	 * 	@param 	url			请求地址
	 * 	@param 	httpMethod	请求方法
	 * 	@return	HttpClientBuilder
	 */
	public static HttpClientBuilder builder(String url, HttpMethod httpMethod) {
		return new HttpClientBuilder(url, httpMethod);
	}
}
